/**
 * ExtDirect4Java
 * @author Maxim Egorov
 * @version 0.2-alpha
 *
 * Copyright(c) 2009, Swissdotnet S?rl
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */
package ch.swissdotnet.extdirect4j;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


public class NamedAction extends AbstractAction {
    private final Map<NamedMethod, NamedMethod> namedMethodMap = new HashMap<NamedMethod, NamedMethod>();

    class NamedMethod {
        private final String methodName;
        private final int argNum;

        NamedMethod(final String methodName, final int argNum) {
            this.methodName = methodName;
            this.argNum = argNum;
        }

        @Override
        public boolean equals(final Object obj) {
            if (obj instanceof NamedMethod) {
                NamedMethod nm = (NamedMethod) obj;
                return nm.methodName.equals(this.methodName) && nm.argNum == this.argNum;
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return (this.methodName + this.argNum).hashCode();
        }
    }

    public NamedAction(final Object instance) {
        super(instance);
    }

    public NamedAction(final Class clazz) {
        super(clazz);
    }

    public NamedAction(final String className) {
        super(className);
    }

    public NamedAction(final String remoteName, final Class clazz) {
        super(clazz);
        this.setRemoteName(remoteName);
    }

    public NamedAction(final String remoteName, final String className) {
        super(className);
        this.setRemoteName(remoteName);
    }

    public NamedAction(final String remoteName, final Class clazz, final boolean lazyInit) {
        this(remoteName, clazz);
        this.setLazyInit(lazyInit);
    }

    public NamedAction(final String remoteName, final String className, final boolean lazyInit) {
        this(remoteName, className);
        this.setLazyInit(lazyInit);
    }

    public NamedAction addMethod(final String methodName, final int argNum) {
        NamedMethod namedMethod = new NamedMethod(methodName, argNum);
        if (this.namedMethodMap.containsKey(namedMethod)) {
            throw new ExtDirectException("duplicate declaration method: " + methodName + " with same parameters number");
        }
        this.namedMethodMap.put(namedMethod, namedMethod);
        return this;
    }

    void beforeApply() {
        MethodResolver resolver = new MethodResolver() {
            ExtDirectMethod resolve(Method method) {
                NamedMethod newNamedMethod = new NamedMethod(method.getName(), method.getParameterTypes().length);
                NamedMethod foundMethod = NamedAction.this.namedMethodMap.get(newNamedMethod);
                if (foundMethod != null) {
                    return new ExtDirectMethod(method);
                }
                return null;
            }
        };
        this.applyMethodsRecursively(this.getClazz(), resolver);
        this.namedMethodMap.clear();
    }
}
